package org.hepeng.workx.util.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import org.hepeng.workx.util.ClassUtils;
import org.hepeng.workx.util.ConstructorUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;

/**
 * @author he peng
 */
public class CglibProxyFactory implements ProxyFactory {

    @Override
    public Object createProxy(Object target , ProxyInvocationHandler invocationHandler, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return createProxy(target.getClass() , invocationHandler , constructorArgTypes , constructorArgs);
    }

    @Override
    public Object createProxy(Class targetClass, ProxyInvocationHandler invocationHandler, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        return createProxy(targetClass , null , invocationHandler , constructorArgTypes , constructorArgs);
    }

    @Override
    public Object createProxy(Class targetClass, List<Class<?>> interfaces, ProxyInvocationHandler invocationHandler, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        Enhancer enhancer = new Enhancer();
        if (! ClassUtils.isInterface(targetClass)) {
            enhancer.setSuperclass(targetClass);
            enhancer.setInterfaces(ConstructorUtils.argTypeToArray(interfaces));
        } else {
            if (Objects.isNull(interfaces)) {
                interfaces = new ArrayList<>();
            }
            interfaces.add(targetClass);
            interfaces = new ArrayList<>(new HashSet<>(interfaces));
            enhancer.setInterfaces(ConstructorUtils.argTypeToArray(interfaces));
        }

        Object[] args = ConstructorUtils.argToArray(constructorArgs);
        Class<?>[] argTypes = ConstructorUtils.argTypeToArray(constructorArgTypes);
        enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
            CglibInvocation cglibInvocation = new CglibInvocation(obj , method , args1 , proxy);
            return invocationHandler.invoke(cglibInvocation);
        });
        return enhancer.create(argTypes, args);
    }
}
